﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DonutLandLibrary.Robots.Environments;
using System.Data.Entity;

namespace DonutLandLibrary.Robots
{
    public class Robot
    {        
        public enum Direction
        {
            N,
            NE,
            E,
            SE,
            S,
            SW,
            W,
            NW,
        };

        private GeneticAlgorithm.GeneticProfile geneticProfile;
        private Square currentSquare;
        private int protectiveCasing;
        private int energyLevel;
        private bool robotAlive;
        private int energyDepletionReserve;
        private int protectiveCasingReserve;
        private RobotEnvironment robotEnvironment;
        private Direction direction;
        private int cycleAdded;
        private int currentCycle;

        public GeneticAlgorithm.GeneticProfile GeneticProfile
        {
            get
            {
                return geneticProfile;
            }
            set
            {
                geneticProfile = value;
            }
        }

        #region Properties

        public int Fitness
        {
            get
            {
                return (int)((currentCycle - cycleAdded) + energyLevel + protectiveCasing) / 3;
            }
        }

        public Direction RobotDirection
        {
            get
            {
                return direction;
            }
            set
            {
                direction = value;
            }
        }

        public int ID { get; set; }

        public int ProtectiveCasingReserve
        {
            get
            {
                return protectiveCasingReserve;
            }
            set
            {
                protectiveCasingReserve = value;
            }
        }

        public int EnergyDepletionReserve
        {
            get
            {
                return energyDepletionReserve;
            }
            set
            {
                energyDepletionReserve = value;
            }
        }

        public Square CurrentSquare
        {
            get
            {
                return currentSquare;
            }
            set
            {
                currentSquare = value;
            }
        }

        public int ProtectiveCasing
        {
            get
            {
                return protectiveCasing;
            }
            set
            {
                protectiveCasing = value;
            }
        }

        public int EnergyLevel
        {
            get
            {
                return energyLevel;
            }
            set
            {
                energyLevel = value;
            }
        }

        public bool RobotAlive
        {
            get
            {
                return robotAlive;
            }
            set
            {
                robotAlive = value;
            }
        }

        public RobotEnvironment CurrentEnvironment
        {
            get
            {
                return robotEnvironment;
            }
            set
            {
                robotEnvironment = value;
            }
        }

        public int X
        {
            get
            {
                return currentSquare.XCoordinate;
            }
        }

        public int Y
        {
            get
            {
                return currentSquare.YCoordinate;
            }
        }

        public int CycleAdded;

        #endregion
        
        public Robot(GeneticAlgorithm.GeneticProfile geneProfile, int cycleCount, int currentCycleCount){

            direction = RandomGenerator.getInstance().RandomDirection();

            cycleAdded = cycleCount;
            currentCycle = currentCycleCount;

            energyDepletionReserve = geneProfile.EnergyDepletionReserve;
            ProtectiveCasingReserve = geneProfile.ProtectiveCasingRepairReserve;

            energyLevel = 0;
            protectiveCasing = geneProfile.MaximumProtectiveCasing;

            robotAlive = true;

            geneticProfile = geneProfile;
        }

        public SquareCollection UpdateRobot(SquareCollection squares, int currentCycleCount){
            //Move!
            bool moveSuccesful = false;
            currentCycle = currentCycleCount;

            while (!moveSuccesful)
            {
                moveSuccesful = Move(direction, 1, ref squares);
            }

            if (currentSquare.CorrosivePatchPresent)
            {
                protectiveCasing--;
            }

            if (currentSquare.PowerSources > 0)
            {
                if (energyLevel < geneticProfile.MaximumEnergyLevel)
                {
                    energyLevel++;
                    currentSquare.PowerSources--;
                }
            }

            //method to calculate the new energy level
            calculateEnergyLevel();

            //method to calculate the new protective casing level
            calculateProtectiveCasing();

            //if the robot is out of energy, then the robot dies
            if (energyLevel < 0)
            {
                robotAlive = false;
                currentSquare.RobotsOnSquare.Remove(this);
            }

            //if the robot casing is destroyed
            if (protectiveCasing < 0)
            {
                robotAlive = false;
                currentSquare.RobotsOnSquare.Remove(this);
            }

            return squares;
        }

        private void calculateEnergyLevel()
        {
            energyDepletionReserve -= geneticProfile.EnergyDepletionRate;

            if (energyDepletionReserve < 0)
            {
                energyLevel--;

                energyDepletionReserve = geneticProfile.EnergyDepletionReserve;
            }
        }

        private void calculateProtectiveCasing()
        {
            protectiveCasingReserve -= geneticProfile.ProtectiveCasingRepairRate;

            if (protectiveCasingReserve < 0)
            {
                if (protectiveCasing < geneticProfile.MaximumProtectiveCasing)
                {
                    protectiveCasing++;

                    protectiveCasingReserve = geneticProfile.ProtectiveCasingRepairReserve;
                }
            }
        }

        public bool detectEnergySource()
        {
            bool powerSourcesDetected = false;

            if (currentSquare.PowerSources > 0)
            {
                powerSourcesDetected = true;
            }

            return powerSourcesDetected;
        }

        private bool Move(Direction direction, int moveDistance, ref SquareCollection squares)
        {
            int newYCoordinate = currentSquare.YCoordinate;
            int newXCoordinate = currentSquare.XCoordinate;
            Square destinationSquare;
            bool moveSuccess = false;

            squares.Reset();

            switch (direction)
            {
                case Direction.N:
                    newYCoordinate = currentSquare.YCoordinate - moveDistance;
                    break;
                case Direction.NE:
                    newYCoordinate = currentSquare.YCoordinate - moveDistance;
                    newXCoordinate = currentSquare.XCoordinate + moveDistance;
                    break;
                case Direction.E:
                    newXCoordinate = currentSquare.XCoordinate + moveDistance;
                    break;
                case Direction.SE:
                    newYCoordinate = currentSquare.YCoordinate + moveDistance;
                    newXCoordinate = currentSquare.XCoordinate + moveDistance;
                    break;
                case Direction.S:
                    newYCoordinate = currentSquare.YCoordinate + moveDistance;
                    break;
                case Direction.SW:
                    newYCoordinate = currentSquare.YCoordinate + moveDistance;
                    newXCoordinate = currentSquare.XCoordinate - moveDistance;
                    break;
                case Direction.W:
                    newXCoordinate = currentSquare.XCoordinate - moveDistance;
                    break;
                case Direction.NW:
                    newYCoordinate = currentSquare.YCoordinate - moveDistance;
                    newXCoordinate = currentSquare.XCoordinate - moveDistance;
                    break;
            }

            newXCoordinate = RobotEnvironment.CheckRowCoordinatesForWrap(newXCoordinate);
            newYCoordinate = RobotEnvironment.CheckColumnCoordinatesForWrap(newYCoordinate);

            destinationSquare = squares.retrieveSquareAtCoordinate(newXCoordinate, newYCoordinate);

            currentSquare.RobotsOnSquare.Remove(this);

            currentSquare = destinationSquare;

            currentSquare.RobotsOnSquare.Add(this);

            moveSuccess = true;

            return moveSuccess;
        }
    }
}
